package com.jcake.vimlike.internal.commands;

import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.texteditor.ITextEditor;

import com.jcake.vimlike.api.ICommand;
import com.jcake.vimlike.internal.CommandParser;
import com.jcake.vimlike.internal.TextUtils;

public abstract class AbstractRegisterCountCommand implements ICommand {

	public Status accept(String command) {
		IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (activeWorkbenchWindow == null) {
			return Status.NO;
		}
		IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage();
		if (activePage.getActivePart() != null && !(activePage.getActivePart() instanceof IEditorPart)) {
			return Status.NO;
		}
		IEditorPart activeEditor = activePage.getActiveEditor();
		if (activeEditor == null) {
			return Status.NO;
		}
		CommandParser parser = new CommandParser(command);
		String c = parser.getCommand();
		if (c != null) {
			for (String fullCommand : getFullCommands()) {
				if (fullCommand.equals(c)) {
					return Status.YES;
				} else if (fullCommand.startsWith(c)) {
					return Status.MAYBE;
				}
			}
			return Status.NO;
		}

		if (parser.getCount() != null || parser.getRegister() != null) {
			return Status.MAYBE;
		}
		return Status.MAYBE;
	}

	protected abstract String[] getFullCommands();

	protected ITextSelection getSelection() {
		ITextEditor textEditor = getTextEditor();
		if (textEditor == null) {
			return null;
		}
		return (ITextSelection) textEditor.getSelectionProvider().getSelection();
	}

	protected ITextEditor getTextEditor() {
		IEditorPart activeEditor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		if (!(activeEditor instanceof ITextEditor)) {
			return null;
		}
		ITextEditor textEditor = (ITextEditor) activeEditor;
		return textEditor;
	}

	protected IDocument getDocument() {
		ITextEditor textEditor = getTextEditor();
		return textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
	}

	protected Region findLines(IDocument document, int offset, Integer count) throws BadLocationException {
		int startLine = document.getLineOfOffset(offset);
		int endLine = Math.min(startLine + (count - 1), getDocument().getNumberOfLines());
		int startOffset = document.getLineOffset(startLine);
		IRegion endLineInfo = document.getLineInformation(endLine);
		String lineDelimiter = document.getLineDelimiter(endLine);
		int endOffset = endLineInfo.getOffset() + endLineInfo.getLength();
		if (lineDelimiter != null) {
			endOffset += lineDelimiter.length();
		}
		int length = endOffset - startOffset;
		return new Region(startOffset, length);
	}

	protected Region findWords(IDocument document, int offset, Integer count) throws BadLocationException {
		IRegion lineInfo = document.getLineInformationOfOffset(offset);
		String restOfTheText = document.get(lineInfo.getOffset(), document.getLength() - lineInfo.getOffset());
		int cursorOffset = offset - lineInfo.getOffset();
		if (cursorOffset == restOfTheText.length()) {
			if (restOfTheText.length() > 0) {
				cursorOffset -= 1;
			}
		}
		int firstWordIndex = TextUtils.findFirstWordPosition(restOfTheText, cursorOffset);
		int endIdx = TextUtils.findNthWordEnd(restOfTheText, firstWordIndex, count);
		int startOffset = lineInfo.getOffset() + firstWordIndex;
		int length = (endIdx - firstWordIndex) + 1;
		return new Region(startOffset, length);
	}

	protected Region findSentences(IDocument document, int offset, Integer count) throws BadLocationException {
		String content = document.get();
		// 1. move backwards to find sentence starts before offset
		// sentence start may be:
		// - blank line,
		// - nonwhitespace after .|!|?  followed by optional )|]|"|' followed by whitespace
		// - BOF
		int start = TextUtils.findSentenceStartBefore(content, offset);
		if (start == -1) {
			return new Region(-1, -1);
		}
		// 2. find [count] sentence endings after position found at 1.
		// sentence end may be:
		// - blank line,
		// - .|!|?  followed by optional )|]|"|' followed by whitespace
		// - EOF
		int end = TextUtils.findNSentencesEnd(content, start, count);

		return new Region(start, (end + 1) - start);
	}

	protected Region findParagraphs(IDocument document, int offset, Integer count) throws BadLocationException {
		String text = document.get();
		int start = TextUtils.findParagraphStartBefore(text, offset);
		int end = TextUtils.findNParagraphEndAfter(text, start, count);
		return new Region(start, end - start);
	}

	protected Region findRegionToEndOfLine(IDocument document, int offset, int count) {
		try {
			int firstLine = document.getLineOfOffset(offset);
			IRegion firstLineInformation = document.getLineInformation(firstLine);

			int lastLine = Math.min(document.getNumberOfLines() - 1, firstLine + (count - 1));
			IRegion lastLineInformation = document.getLineInformation(lastLine);

			return new Region(offset, lastLineInformation.getOffset() - firstLineInformation.getOffset() + lastLineInformation.getLength() - (offset - firstLineInformation.getOffset()));
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		return new Region(-1, -1);
	}

	protected Region findRegionFromBeginingOfLinesBackToOffset(IDocument document, int offset, Integer count) {
		try {
			int lastLine = document.getLineOfOffset(offset);
			int firstLine = Math.max(0, lastLine - (count - 1));
			IRegion firstLineInformation = document.getLineInformation(firstLine);

			return new Region(firstLineInformation.getOffset(), offset - firstLineInformation.getOffset());
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		return null;
	}

	protected void notice(String message) {
		IEditorSite editorSite = getTextEditor().getEditorSite();
		IActionBars actionBar = editorSite.getActionBars();
		IStatusLineManager statusLineManager = actionBar.getStatusLineManager();
		statusLineManager.setMessage(message);
	}
}
